Phase 2 trial distinct arms

Example phase 2 virtual trial for BMP4 + RT with distinct treatment arm populations
Authors

Nicholas Harbour

Markus Owen

Published

July 31, 2024, 11:55:38

Modified

July 31, 2024, 11:34:36

1 import packages and define functions

Code
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import scipy.stats as stats
from scipy.stats import lognorm
from numba import jit
from sksurv.compare import compare_survival
from sksurv.nonparametric import kaplan_meier_estimator

all functions are in: “notebooks/gsc_model_functions.py”

Code
import gsc_model_functions as gmf

For this we need to load in the patient data and calualte the distribution params

Code
# load in the patient survival data
historic_df = pd.read_csv("Rho_D_data.csv")
censored_str = "Censorship (1=censored)"
survival_str = "Overall Survival"
# only keep patients that weren't censored
historic_df = historic_df[historic_df[censored_str] == 0]
# cut off patients that had very high proliferation rate
historic_df = historic_df[historic_df["PIHNA rho"] < 100 ] 
historic_df[censored_str] = True

# fit distribution to the data
dist_name = 'lognorm'  # Replace with the desired distribution name
dist = getattr(stats, dist_name)
params = dist.fit(historic_df["PIHNA rho"])
    
shape = params[0]
loc = params[1]
scale = params[-1]

2 Phase 2 virtual trial

For phase 2 trial we will assume a heterogenous population, so they will have differnt values of \(\psi\) based on some distribution. We will assume this is a truncated normal distibution. For different means of this normal distribution we will calculate the probability of a successful trial (significantly different from the virtual control).

3 Run trial with distinct BMP4 and no-BMP4 treatment populations

Code
n_trials=20
n_patients=20

psi_mean_values,frac_succ,BMP4_mean_survival,noBMP4_mean_survival,mean_rho_BMP4,mean_rho_noBMP4,p_values,figs = gmf.phase2_trial_fun(n_trials,n_patients, distinct_arms=True, rho_case=2, shape=shape, loc=loc, scale=scale)

frac_succ_np20 = frac_succ
Figure 1

Plot the probability of successful trial vs mean \(\psi\).

Code
plt.figure()
plt.plot(psi_mean_values,frac_succ, 'o-') 
plt.xlabel(r"Mean $\psi$")
plt.ylabel("Fraction of successful trials")
plt.ylim(-0.1,1.1)
plt.title('Distinct populations, ' + str(n_patients) + ' patients')
plt.show()
plot of fraction of successful trials for different mean psi values
Figure 2: Fraction of successful trials for different mean psi values where the treatment arms with and without BMP4 are distinct sets drawn from the same distribution, rather than identical.

3.0.1 Plot mean survival vs mean psi values

Expect survival to increase as psi and hence BMP4 effect increases. We do see this within each trial, but the differences between trials are larger.

Code
plt.plot(psi_mean_values,BMP4_mean_survival,'-+')
plt.legend(['0','1','2','3','4','5','6','7','8','9'])
plt.plot(psi_mean_values,noBMP4_mean_survival,'--x')
plt.xlabel(r'Mean $\psi$')
plt.ylabel('Mean survival')
plt.title('Distinct populations, ' + str(n_patients) + ' patients')
Text(0.5, 1.0, 'Distinct populations, 20 patients')

3.0.2 Plot p-values and mean survival vs mean proliferation rate

We expect as proliferation rate increases the difference due to BMP4 treatment would increase.

Code
fig, (ax1,ax2) = plt.subplots(2, sharex=True) 

ax1.plot(mean_rho_BMP4.T,BMP4_mean_survival.T,'*')
ax1.plot(mean_rho_noBMP4[0,:],noBMP4_mean_survival[0,:],'o',alpha=0.5)
ax1.set(ylabel='Mean survival')

ax2.plot(mean_rho_BMP4.T,p_values.T,'*')
ax2.plot(mean_rho_noBMP4.T,p_values.T,'o',alpha=0.5)
ax2.set(xlabel=r'Mean $\rho$')
ax2.set(ylabel='p-value')

ax2.legend([r'$\psi$=0',r'$\psi$=0.5',r'$\psi$=5',r'$\psi$=50','no BMP4'])

fig.suptitle('Distinct populations, ' + str(n_patients) + ' patients')
Text(0.5, 0.98, 'Distinct populations, 20 patients')
(a)
Figure 3

3.0.3 Plot including differences between BMP and no-BMP populations

Code
fig, axs = plt.subplots(2, 2, sharex=True, sharey=True)

for i in range(len(psi_mean_values)) :
    fig.axes[i].plot(np.array([mean_rho_BMP4[i,:],mean_rho_noBMP4[i,:]]),np.array([p_values[i,:],p_values[i,:]]),'-')
    fig.axes[i].plot((mean_rho_BMP4[i,:]+mean_rho_noBMP4[i,:])/2,p_values[i,:],'*')
    fig.axes[i].axhspan(0, 0.05, alpha=0.25)
    fig.axes[i].set_title(r'$\psi$='+str(psi_mean_values[i]), x=0.95, y=0.92, ha="right", va="top")

fig.supxlabel(r'Mean $\rho$')
fig.supylabel('p-value')
fig.suptitle(r'p-value vs mean $\rho$, with and without BMP4 treatment' + '\n Distinct populations, ' + str(n_patients) + ' patients')
Text(0.5, 0.98, 'p-value vs mean $\\rho$, with and without BMP4 treatment\n Distinct populations, 20 patients')

Code
fig, axs = plt.subplots(2, 2, sharex=True, sharey=True)

for i in range(len(psi_mean_values)) :
    fig.axes[i].plot(np.array([mean_rho_BMP4[i,:],mean_rho_noBMP4[i,:]]),np.array([BMP4_mean_survival[i,:],noBMP4_mean_survival[i,:]]),'-k',alpha=0.5)
    fig.axes[i].plot(mean_rho_BMP4[i,:],BMP4_mean_survival[i,:],'og',alpha=0.5,label='BMP4')
    fig.axes[i].plot(mean_rho_noBMP4[i,:],noBMP4_mean_survival[i,:],'or',alpha=0.5,label='no BMP4')
    fig.axes[i].set_title(r'$\psi$='+str(psi_mean_values[i]), x=0.95, y=0.92, ha="right", va="top")

fig.supxlabel(r'Mean $\rho$')
fig.supylabel('Mean survival')
fig.suptitle(r'Survival vs mean $\rho$, with and without BMP4 treatment' + '\n Distinct populations, ' + str(n_patients) + ' patients')

fig.axes[0].legend(loc = 'upper center')

4 Repeat with distinct populations and more patients

Code
n_trials=20
n_patients=30

psi_mean_values,frac_succ,BMP4_mean_survival,noBMP4_mean_survival,mean_rho_BMP4,mean_rho_noBMP4,p_values,figs = gmf.phase2_trial_fun(n_trials,n_patients, distinct_arms=True, rho_case=2, shape=shape, loc=loc, scale=scale)

frac_succ_np30 = frac_succ
Figure 4

Plot the probability of successful trial vs mean \(\psi\).

Code
plt.figure()
plt.plot(psi_mean_values,frac_succ, 'o-') 
plt.xlabel(r"Mean $\psi$")
plt.ylabel("Fraction of successful trials")
plt.ylim(-0.1,1.1)
plt.title('Distinct populations, ' + str(n_patients) + ' patients')
plt.show()
plot of fraction of successful trials for different mean psi values
Figure 5: Fraction of successful trials for different mean psi values where the treatment arms with and without BMP4 are distinct sets drawn from the same distribution, rather than identical.

4.0.1 Plot mean survival vs mean psi values

Expect survival to increase as psi and hence BMP4 effect increases. We do see this within each trial, but the differences between trials are larger.

Code
plt.plot(psi_mean_values,BMP4_mean_survival,'-+')
plt.legend(['0','1','2','3','4','5','6','7','8','9'])
plt.plot(psi_mean_values,noBMP4_mean_survival,'--x')
plt.xlabel(r'Mean $\psi$')
plt.ylabel('Mean survival')
plt.title('Distinct populations, ' + str(n_patients) + ' patients')
Text(0.5, 1.0, 'Distinct populations, 30 patients')

4.0.2 Plot p-values and mean survival vs mean proliferation rate

We expect as proliferation rate increases the difference due to BMP4 treatment would increase.

Code
fig, (ax1,ax2) = plt.subplots(2, sharex=True) 

ax1.plot(mean_rho_BMP4.T,BMP4_mean_survival.T,'*')
ax1.plot(mean_rho_noBMP4[0,:],noBMP4_mean_survival[0,:],'o',alpha=0.5)
ax1.set(ylabel='Mean survival')

ax2.plot(mean_rho_BMP4.T,p_values.T,'*')
ax2.plot(mean_rho_noBMP4.T,p_values.T,'o',alpha=0.5)
ax2.set(xlabel=r'Mean $\rho$')
ax2.set(ylabel='p-value')

ax2.legend([r'$\psi$=0',r'$\psi$=0.5',r'$\psi$=5',r'$\psi$=50','no BMP4'])

fig.suptitle('Distinct populations, ' + str(n_patients) + ' patients')
Text(0.5, 0.98, 'Distinct populations, 30 patients')
(a)
Figure 6

4.0.3 Plot including differences between BMP and no-BMP populations

Code
fig, axs = plt.subplots(2, 2, sharex=True, sharey=True)

for i in range(len(psi_mean_values)) :
    fig.axes[i].plot(np.array([mean_rho_BMP4[i,:],mean_rho_noBMP4[i,:]]),np.array([p_values[i,:],p_values[i,:]]),'-k',alpha=0.5)
    fig.axes[i].plot(mean_rho_BMP4[i,:],p_values[i,:],'og',alpha=0.5,label='BMP4')
    fig.axes[i].plot(mean_rho_noBMP4[i,:],p_values[i,:],'or',alpha=0.5,label='no BMP4')

    fig.axes[i].axhspan(0, 0.05, alpha=0.25)
    fig.axes[i].set_title(r'$\psi$='+str(psi_mean_values[i]), x=0.95, y=0.92, ha="right", va="top")

fig.supxlabel(r'Mean $\rho$')
fig.supylabel('p-value')
fig.suptitle(r'p-value vs mean $\rho$, with and without BMP4 treatment' + '\n Distinct populations, ' + str(n_patients) + ' patients')
fig.axes[3].legend(loc = 'upper left')

Code
fig, axs = plt.subplots(2, 2, sharex=True, sharey=True)

for i in range(len(psi_mean_values)) :
    fig.axes[i].plot(np.array([mean_rho_BMP4[i,:],mean_rho_noBMP4[i,:]]),np.array([BMP4_mean_survival[i,:],noBMP4_mean_survival[i,:]]),'-k',alpha=0.5)
    fig.axes[i].plot(mean_rho_BMP4[i,:],BMP4_mean_survival[i,:],'og',alpha=0.5,label='BMP4')
    fig.axes[i].plot(mean_rho_noBMP4[i,:],noBMP4_mean_survival[i,:],'or',alpha=0.5,label='no BMP4')
    fig.axes[i].set_title(r'$\psi$='+str(psi_mean_values[i]), x=0.95, y=0.92, ha="right", va="top")

fig.supxlabel(r'Mean $\rho$')
fig.supylabel('Mean survival')
fig.suptitle(r'Survival vs mean $\rho$, with and without BMP4 treatment' + '\n Distinct populations, ' + str(n_patients) + ' patients')
fig.axes[0].legend(loc = 'upper center')

5 Repeat with distinct populations and more n_patients=40

Code
n_trials=20
n_patients=40

psi_mean_values,frac_succ,BMP4_mean_survival,noBMP4_mean_survival,mean_rho_BMP4,mean_rho_noBMP4,p_values,figs = gmf.phase2_trial_fun(n_trials,n_patients, distinct_arms=True, rho_case=2, shape=shape, loc=loc, scale=scale)

frac_succ_np40 = frac_succ
Figure 7
Code
n_trials=20
n_patients=50

psi_mean_values,frac_succ,BMP4_mean_survival,noBMP4_mean_survival,mean_rho_BMP4,mean_rho_noBMP4,p_values,figs = gmf.phase2_trial_fun(n_trials,n_patients, distinct_arms=True, rho_case=2, shape=shape, loc=loc, scale=scale)

frac_succ_np50 = frac_succ
Figure 8
Code
plt.figure()
plt.plot(psi_mean_values,frac_succ_np20, 'o-',label=r'$n_{patients}$=20') 
plt.plot(psi_mean_values,frac_succ_np30, 'o-',label=r'$n_{patients}$=30') 
plt.plot(psi_mean_values,frac_succ_np40, 'o-',label=r'$n_{patients}$=40') 
plt.plot(psi_mean_values,frac_succ_np50, 'o-',label=r'$n_{patients}$=50') 
plt.xlabel(r"Mean $\psi$")
plt.ylabel("Fraction of successful trials")
plt.ylim(-0.1,1.1)
plt.title('Distinct populations')
plt.legend(loc = 'lower right')
plt.show()
plot of fraction of successful trials for different mean psi values
Figure 9: Fraction of successful trials for different mean psi values where the treatment arms with and without BMP4 are distinct sets drawn from the same distribution, rather than identical.